from smake_db import *
import logging

# ===CODE===

# 【注意】lazy_judege的判断理由都用debug日志打出来

# !!!???define option

class LazyJudge:
    def __init__(self, is_force):
        self.is_force = is_force
        self.changed_fnames = set()
        self.changed_proj_names = set()
        # self.proj_need_build = {} # {proj_name : bool}
        self.proj_need_link = {} # {proj_name : bool}
                
    def update_is_change(self):
        '''根据当前文件修改时间，判断文件和项目是否改变，并记录'''
        for proj_name in g_db.proj_files:
            for fname in g_db.proj_files[proj_name]:
                if g_db.get_file_mtime(fname) != g_db.get_history_file_mtimes(fname):
                    self.changed_fnames.add(fname)
                    self.changed_proj_names.add(proj_name)

    def need_gen_mgc(self, proj_name) -> bool:
        '''判断项目是否需要生成mgc'''
        raise NotImplementedError()
    
    def is_file_need_analyze(self, proj_name, file_name):
        # 配置文件改了（可能修改了构建参数的宏定义，等）
        reason = ''
        if self.__is_global_conf_change() \
        or self.__is_proj_conf_change(proj_name):
            reason = 'conf changed'
        # 文件修改了
        elif file_name in self.changed_fnames:
            reason = 'modified'
        # 依赖的文件改了
        else:
            deps = g_db.get_file_rely(file_name)
            for dep_file_name in deps:
                if dep_file_name in self.changed_fnames:
                    reason = 'dep file modified : ' + dep_file_name
                    break
        if reason != '':
            logging.debug('file [ %s ] need analyise, reason: %s'%(file_name, reason))
            return True
        return False

    def is_file_need_compile(self, proj_name, file_name):
        '''判断源文件是否需要编译，需要时会打印日志说明原因'''
        reason = ''
        tmp = []
        # force选项开启
        if self.is_force == True:
            reason = 'force'
        # 源文件上次编译失败
        elif g_db.get_history_file_success(file_name) != True:
            reason = 'compile failed'
        # 全局配置修改
        elif self.__is_global_conf_change():
            reason = 'global conf changed'
        # 项目配置修改
        elif self.__is_proj_conf_change(proj_name):
            reason = 'proj conf changed'
        # 源文件修改
        elif file_name in self.changed_fnames:
            reason = 'file changed'
        # 依赖的头文件更新
        elif self.__is_dep_files_update(file_name, tmp):
            reason = 'dep changed : %s'%(tmp[0])
        if reason != '':
            logging.debug('file: %s need compile. reason: %s'%(file_name, reason))
            return True
        return False

    def is_proj_need_link(self, proj_name, dep_proj_names:list, compile_cmd_count:int):
        '''判断项目是否需要链接或打包成库，必须按照拓扑排序的顺序调用
        Args:
            proj_name: 项目名
            dep_proj_names: 依赖的项目名列表
            compile_cmd_count: 本项目此次构建时，生成的编译cmd的个数
        Return:
            返回需要链接的理由'''
        reason = ''
        # 自己变了（有编译命令）
        if compile_cmd_count > 0:
            reason = 'self compiled'
        # 项目失败了
        elif g_db.get_history_proj_success(proj_name) == False:
            reason = 'proj failed'
        # 源文件有增减（主要是减少的情况）
        # elif g_db.is_src_file_count_changed(proj_name): # 这里有问题。。。
        #     reason = 'file count changed'
        # 依赖的项目link了
        else:
            for name in dep_proj_names:
                if name in self.proj_need_link \
                and self.proj_need_link[name] == True:
                    reason = 'dep proj(%s) linked'%(name)
                    break
        if reason == '':
            self.proj_need_link[proj_name] = False
            return False
        logging.debug('proj (%s) need link, reason: %s'%(proj_name, reason))
        self.proj_need_link[proj_name] = True
        return True
    
    def __is_global_conf_change(self):
        return g_db.get_global_conf_md5() != g_db.get_history_global_conf_md5()

    def __is_proj_conf_change(self, proj_name):
        return g_db.get_proj_conf_md5(proj_name) != g_db.get_history_proj_conf_md5(proj_name)

    def __is_dep_files_update(self, file_name, changed_list:list):
        '''判断源文件依赖的所有文件中，是否有更新
        
        Args:
            changed_list: [OUT] 返回True时，changed_list返回修改的文件的名称（部分）'''
        changed_list.clear()
        for dep_fname in g_db.get_file_rely(file_name):
            if dep_fname in self.changed_fnames:
                changed_list.append(dep_fname)
                return True
        return False


        

